﻿/*
 * Please leave this Copyright notice in your code if you use it
 * Written by Decebal Mihailescu [http://www.codeproject.com/script/articles/list_articles.asp?userid=634640]
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.Diagnostics;

namespace CommonUtils
{
    using System.Reflection;
    using System.Text.RegularExpressions;
    using System.ComponentModel.Composition;
    using System.ComponentModel;
    using System.Globalization;
    using Microsoft.Win32;
    using System.Runtime.InteropServices;
    using System.IO;
    using System.Security.AccessControl;

    using MyTextImport = Lazy<ITextImport, ITextImportMetaData>;

    public interface ITextManipulation
    {
        // Methods
        string PreTranslation(string frg);
        string PostTranslation(string frg);
        IList<string[]> GetSplitedText(string text);
        string LogoFormat
        {
            get;
        }


    }

    public static class PersistedSharedData
    {



        public static string DefaultPlugInDirectory
        {
            set
            {
                try
                {
                    RegistryKey Key = Registry.CurrentUser.OpenSubKey(_registryDirectoriesKeyInfo, true);
                    if (Key == null)
                        Key = Registry.CurrentUser.CreateSubKey(_registryDirectoriesKeyInfo, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    Key.SetValue(_registryDefaultPlugInLocation, value, RegistryValueKind.String);
                    Key.Close();
#if DEBUG
                    System.Windows.Forms.MessageBox.Show(string.Format("_registryDirectoriesKeyInfo = {0} \r\n _registryDefaultPlugInLocation {1} \r\n value = {2}",
                        _registryDirectoriesKeyInfo, _registryDefaultPlugInLocation, value), "set DefaultPlugInDirectory");
#endif
                }
                catch (Exception ex)
                {
                    Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                }
                finally
                {
                    Trace.Flush();
                }
            }
            get
            {
                try
                {
                    RegistryKey Key = Registry.CurrentUser.OpenSubKey(_registryDirectoriesKeyInfo);
                    if (Key == null)
                        return null;
                    string value = Key.GetValue(_registryDefaultPlugInLocation) as string;
                    Key.Close();
#if DEBUG
                    System.Windows.Forms.MessageBox.Show(string.Format("_registryDirectoriesKeyInfo = {0} \r\n _registryDefaultPlugInLocation {1} \r\n value = {2}",
                        _registryDirectoriesKeyInfo, _registryDefaultPlugInLocation, value), "get DefaultPlugInDirectory");
#endif
                    return value;
                }
                catch (Exception ex)
                {
                    Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                    return null;
                }
                finally
                {
                    Trace.Flush();
                }
            }
        }
        internal const string _registryVersionInfo = @"Software\FreeLanguageTranslator";
        internal const string _registryVersionValue = "Version";
        public static string Version
        {
            set
            {
                try
                {
                    RegistryKey Key = Registry.CurrentUser.OpenSubKey(_registryVersionInfo);
                    if (Key == null)
                        Key = Registry.CurrentUser.CreateSubKey(_registryVersionInfo, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    Key = Registry.CurrentUser.OpenSubKey(_registryVersionInfo, true);
                    Key.SetValue(_registryVersionValue, value, RegistryValueKind.String);
                    Key.Close();
                }
                catch (Exception ex)
                {
                    Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                }
                finally
                {
                    Trace.Flush();
                }
            }
            get
            {
                try
                {
                    RegistryKey Key = Registry.CurrentUser.OpenSubKey(_registryVersionInfo);
                    if (Key == null)
                        return null;
                    string val = Key.GetValue(_registryVersionValue) as string;
                    Key.Close();
                    return val;
                }
                catch (Exception ex)
                {
                    Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                    return null;
                }
                finally
                {
                    Trace.Flush();
                }
            }
        }

        internal const string _registryDirectoriesKeyInfo = @"Software\FreeLanguageTranslator\PlugInLocations\Directories";
        internal const string _registryDefaultPlugInLocation = "Default PlugIn Directory";
        public static IEnumerable<string> PlugInDirectories
        {
            get
            {
                try
                {
                    RegistryKey Key = Registry.CurrentUser.OpenSubKey(_registryDirectoriesKeyInfo);
                    if (Key == null)
                        return Enumerable.Empty<string>();
                    string[] valNames = Key.GetValueNames();
                    List<string> lst = new List<string>(valNames.Length);
                    for (int i = 0; i < valNames.Length; i++)
                    {
                        string item = Key.GetValue(valNames[i]) as string;
                        if (!string.IsNullOrEmpty(item) && System.IO.Directory.Exists(item))
                            lst.Add(item);
                    }
                    Key.Close();
                    return lst;
                }
                catch (Exception ex)
                {
                    Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                    return null;
                }
                finally
                {
                    Trace.Flush();
                }
            }
        }

        public static bool AddDirectoryLocation(string name, string location)
        {
            RegistryKey Key = null;
            try
            {
                Key = Registry.CurrentUser.OpenSubKey(_registryDirectoriesKeyInfo);
                if (Key == null)
                    Key = Registry.CurrentUser.CreateSubKey(_registryDirectoriesKeyInfo, RegistryKeyPermissionCheck.ReadWriteSubTree);
                Key = Registry.CurrentUser.OpenSubKey(_registryDirectoriesKeyInfo, true);
                Key.SetValue(name, location, RegistryValueKind.String);
                return true;

            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                return false;
            }
            finally
            {

                if (Key != null)
                    Key.Close();
            }


        }

        internal const string _registryAssembliesKeyInfo = @"Software\FreeLanguageTranslator\PlugInLocations\Assemblies";
        public static IEnumerable<string> PlugInAssemblies
        {
            get
            {
                try
                {
                    RegistryKey Key = Registry.CurrentUser.OpenSubKey(_registryAssembliesKeyInfo);
                    if (Key == null)
                        return Enumerable.Empty<string>();
                    string[] valNames = Key.GetValueNames();
                    List<string> lst = new List<string>(valNames.Length);
                    for (int i = 0; i < valNames.Length; i++)
                    {
                        string item = Key.GetValue(valNames[i]) as string;
                        if (!string.IsNullOrEmpty(item) && System.IO.File.Exists(item))
                            lst.Add(item);
                    }
                    Key.Close();
                    return lst;
                }
                catch (Exception ex)
                {
                    Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                    return null;
                }
                finally
                {
                    Trace.Flush();
                }
            }
        }

        public static bool AddAssemblyLocation(string name, string location)
        {
            RegistryKey Key = null;
            try
            {
                Key = Registry.CurrentUser.OpenSubKey(_registryAssembliesKeyInfo);
                if (Key == null)
                    Key = Registry.CurrentUser.CreateSubKey(_registryAssembliesKeyInfo, RegistryKeyPermissionCheck.ReadWriteSubTree);
                Key = Registry.CurrentUser.OpenSubKey(_registryAssembliesKeyInfo, true);
                Key.SetValue(name, location, RegistryValueKind.String);
                return true;

            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                return false;
            }
            finally
            {
                Trace.Flush();
                if (Key != null)
                    Key.Close();
            }
        }
    }
    public interface ITextImportController
    {
        IEnumerable<MyTextImport> TextProviders
        { get; }
        MyTextImport this[string extension] { get; }
    }
    public class ProgressEventArgs : EventArgs
    {
        public string Description;
        public float Progress = 0.0f;
    }


    static public class EncodingEx
    {
        /// <summary>

        /// Detects the byte order mark of a file and returns

        /// an appropriate encoding for the file.

        /// </summary>

        /// <param name="srcFile"></param>

        /// <returns></returns>

        public static Encoding GetFileEncoding(string srcFile, Encoding fallbackEncoder = null)
        {

            // *** Use Default of Encoding.Default (Ansi CodePage)
            Encoding enc = (fallbackEncoder == null) ? Encoding.Default : fallbackEncoder;

            // *** Detect byte order mark if any - otherwise assume default

            try
            {
                byte[] buffer = new byte[5];

                using (FileStream file = new FileStream(srcFile, FileMode.Open))
                {

                    file.Read(buffer, 0, 5);
                }

                if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
                    enc = Encoding.UTF8;
                else if (buffer[0] == 0xff && buffer[1] == 0xfe)
                    enc = Encoding.Unicode; // utf-16le
                else if (buffer[0] == 0xfe && buffer[1] == 0xff)
                    enc = Encoding.BigEndianUnicode; // utf-16be
                else if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
                    enc = Encoding.UTF32;
                else if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
                    enc = Encoding.UTF7;
            }
            catch
            {
            }


            return enc;

        }

    }

    static public class StringRegEx
    {
        static readonly Regex _eol = new Regex(@"[\r]*\n[\r]*", RegexOptions.Compiled);
        public static string FixEoL(this string input)
        {
            try
            {
                return _eol.Replace(input, "\r\n");
            }
            catch
            {
                return input;
            }
        }
        public static int CountSubStrings(this string input, string delimiter, bool ignoreCase = false)
        {
            int instancesNo = 0;
            int pos = 0;
            while ((pos = input.IndexOf(delimiter, pos, ignoreCase ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture)) != -1)
            {
                pos += delimiter.Length;
                instancesNo++;
            }
            return instancesNo;
        }
        public static string FindFirstMatchingGroup(this string input, string regex, string group, RegexOptions opt = RegexOptions.Multiline)
        {
            string result = null;
            try
            {
                Match m = Regex.Match(input, regex, opt);
                if (m.Success && m.Groups[group].Success)
                {
                    result = m.Groups[group].Value;
                }
            }
            catch
            {

                result = null;
            }
            return result;
        }
    }

    public static class EnumHelper
    {
        public static IList<KeyValuePair<T, string>> EnumToList<T>()
        {
            Type type = typeof(T);
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            if (!type.IsEnum)
            {
                throw new ApplicationException("Argument must be enum");
            }


            Array enumValues = Enum.GetValues(type);
            List<KeyValuePair<T, string>> list = new List<KeyValuePair<T, string>>(enumValues.GetLength(0));
            foreach (Enum value in enumValues)
            {
                list.Add(new KeyValuePair<T, string>((T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture), GetDescription(value)));
            }

            return list;
        }
        public static string GetDescription(Enum value)
        {

            string description = value.ToString();

            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            FieldInfo fieldInfo = value.GetType().GetField(description);

            DescriptionAttribute attribute = Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute))
            as DescriptionAttribute;

            if (attribute != null)
            {
                description = attribute.Description;
            }


            return description;
        }

    }
    static public class ShellIntegration
    {
        //public static string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "LanguageTranslatorInstall.log");
        static ShellIntegration()
        {
            //Trace.Listeners.Add(new TextWriterTraceListener(logPath));
            //Trace.TraceInformation("Started at {0}, Win ver = {1}, Crt dir: '{2}', CLR version ={3}, Is64BitProcess = {4}, Is64BitOperatingSystem = {5}",
            //    DateTime.Now, Environment.OSVersion.Version, Environment.CurrentDirectory, RuntimeEnvironment.GetSystemVersion(), Environment.Is64BitProcess, Environment.Is64BitOperatingSystem);
        }
        #region enum HChangeNotifyEventID
        /// <summary>
        /// Describes the event that has occurred. 
        /// Typically, only one event is specified at a time. 
        /// If more than one event is specified, the values contained 
        /// in the <i>dwItem1</i> and <i>dwItem2</i> 
        /// parameters must be the same, respectively, for all specified events. 
        /// This parameter can be one or more of the following values. 
        /// </summary>
        /// <remarks>
        /// <para><b>Windows NT/2000/XP:</b> <i>dwItem2</i> contains the index 
        /// in the system image list that has changed. 
        /// <i>dwItem1</i> is not used and should be <see langword="null"/>.</para>
        /// <para><b>Windows 95/98:</b> <i>dwItem1</i> contains the index 
        /// in the system image list that has changed. 
        /// <i>dwItem2</i> is not used and should be <see langword="null"/>.</para>
        /// </remarks>
        [Flags]
        enum HChangeNotifyEventID
        {
            /// <summary>
            /// All events have occurred. 
            /// </summary>
            SHCNE_ALLEVENTS = 0x7FFFFFFF,

            /// <summary>
            /// A file type association has changed. <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> 
            /// must be specified in the <i>uFlags</i> parameter. 
            /// <i>dwItem1</i> and <i>dwItem2</i> are not used and must be <see langword="null"/>. 
            /// </summary>
            SHCNE_ASSOCCHANGED = 0x08000000,

            /// <summary>
            /// The attributes of an item or folder have changed. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the item or folder that has changed. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>.
            /// </summary>
            SHCNE_ATTRIBUTES = 0x00000800,

            /// <summary>
            /// A nonfolder item has been created. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the item that was created. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>.
            /// </summary>
            SHCNE_CREATE = 0x00000002,

            /// <summary>
            /// A nonfolder item has been deleted. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the item that was deleted. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_DELETE = 0x00000004,

            /// <summary>
            /// A drive has been added. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the root of the drive that was added. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_DRIVEADD = 0x00000100,

            /// <summary>
            /// A drive has been added and the Shell should create a new window for the drive. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the root of the drive that was added. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_DRIVEADDGUI = 0x00010000,

            /// <summary>
            /// A drive has been removed. <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the root of the drive that was removed.
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_DRIVEREMOVED = 0x00000080,

            /// <summary>
            /// Not currently used. 
            /// </summary>
            SHCNE_EXTENDED_EVENT = 0x04000000,

            /// <summary>
            /// The amount of free space on a drive has changed. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the root of the drive on which the free space changed.
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_FREESPACE = 0x00040000,

            /// <summary>
            /// Storage media has been inserted into a drive. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the root of the drive that contains the new media. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_MEDIAINSERTED = 0x00000020,

            /// <summary>
            /// Storage media has been removed from a drive. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the root of the drive from which the media was removed. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_MEDIAREMOVED = 0x00000040,

            /// <summary>
            /// A folder has been created. <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> 
            /// or <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the folder that was created. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_MKDIR = 0x00000008,

            /// <summary>
            /// A folder on the local computer is being shared via the network. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the folder that is being shared. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_NETSHARE = 0x00000200,

            /// <summary>
            /// A folder on the local computer is no longer being shared via the network. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the folder that is no longer being shared. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_NETUNSHARE = 0x00000400,

            /// <summary>
            /// The name of a folder has changed. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the previous pointer to an item identifier list (PIDL) or name of the folder. 
            /// <i>dwItem2</i> contains the new PIDL or name of the folder. 
            /// </summary>
            SHCNE_RENAMEFOLDER = 0x00020000,

            /// <summary>
            /// The name of a nonfolder item has changed. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the previous PIDL or name of the item. 
            /// <i>dwItem2</i> contains the new PIDL or name of the item. 
            /// </summary>
            SHCNE_RENAMEITEM = 0x00000001,

            /// <summary>
            /// A folder has been removed. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the folder that was removed. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_RMDIR = 0x00000010,

            /// <summary>
            /// The computer has disconnected from a server. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the server from which the computer was disconnected. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// </summary>
            SHCNE_SERVERDISCONNECT = 0x00004000,

            /// <summary>
            /// The contents of an existing folder have changed, 
            /// but the folder still exists and has not been renamed. 
            /// <see cref="HChangeNotifyFlags.SHCNF_IDLIST"/> or 
            /// <see cref="HChangeNotifyFlags.SHCNF_PATH"/> must be specified in <i>uFlags</i>. 
            /// <i>dwItem1</i> contains the folder that has changed. 
            /// <i>dwItem2</i> is not used and should be <see langword="null"/>. 
            /// If a folder has been created, deleted, or renamed, use SHCNE_MKDIR, SHCNE_RMDIR, or 
            /// SHCNE_RENAMEFOLDER, respectively, instead. 
            /// </summary>
            SHCNE_UPDATEDIR = 0x00001000,

            /// <summary>
            /// An image in the system image list has changed. 
            /// <see cref="HChangeNotifyFlags.SHCNF_DWORD"/> must be specified in <i>uFlags</i>. 
            /// </summary>
            SHCNE_UPDATEIMAGE = 0x00008000,

        }
        #endregion // enum HChangeNotifyEventID
        #region public enum HChangeNotifyFlags
        /// <summary>
        /// Flags that indicate the meaning of the <i>dwItem1</i> and <i>dwItem2</i> parameters. 
        /// The uFlags parameter must be one of the following values.
        /// </summary>
        [Flags]
        public enum HChangeNotifyFlags
        {
            /// <summary>
            /// The <i>dwItem1</i> and <i>dwItem2</i> parameters are DWORD values. 
            /// </summary>
            SHCNF_DWORD = 0x0003,
            /// <summary>
            /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of ITEMIDLIST structures that 
            /// represent the item(s) affected by the change. 
            /// Each ITEMIDLIST must be relative to the desktop folder. 
            /// </summary>
            SHCNF_IDLIST = 0x0000,
            /// <summary>
            /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings of 
            /// maximum length MAX_PATH that contain the full path names 
            /// of the items affected by the change. 
            /// </summary>
            SHCNF_PATHA = 0x0001,
            /// <summary>
            /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings of 
            /// maximum length MAX_PATH that contain the full path names 
            /// of the items affected by the change. 
            /// </summary>
            SHCNF_PATHW = 0x0005,
            /// <summary>
            /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings that 
            /// represent the friendly names of the printer(s) affected by the change. 
            /// </summary>
            SHCNF_PRINTERA = 0x0002,
            /// <summary>
            /// <i>dwItem1</i> and <i>dwItem2</i> are the addresses of null-terminated strings that 
            /// represent the friendly names of the printer(s) affected by the change. 
            /// </summary>
            SHCNF_PRINTERW = 0x0006,
            /// <summary>
            /// The function should not return until the notification 
            /// has been delivered to all affected components. 
            /// As this flag modifies other data-type flags, it cannot by used by itself.
            /// </summary>
            SHCNF_FLUSH = 0x1000,
            /// <summary>
            /// The function should begin delivering notifications to all affected components 
            /// but should return as soon as the notification process has begun. 
            /// As this flag modifies other data-type flags, it cannot by used by itself.
            /// </summary>
            SHCNF_FLUSHNOWAIT = 0x2000
        }
        #endregion // enum HChangeNotifyFlags
        [DllImport("shell32.dll")]
        static extern void SHChangeNotify(HChangeNotifyEventID wEventId,
                                           HChangeNotifyFlags uFlags,
                                           IntPtr dwItem1,
                                           IntPtr dwItem2);
        static public void ShellRefresh()
        {
            try
            {
                SHChangeNotify(HChangeNotifyEventID.SHCNE_ASSOCCHANGED, HChangeNotifyFlags.SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);
            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
            }
            finally
            {
                Trace.Flush();
            }
        }
        internal static bool AddFileSecurity(string fileName, FileSystemRights rights = FileSystemRights.Write, AccessControlType controlType = AccessControlType.Allow)
        {


            try
            {
                // Get a FileSecurity object that represents the 
                // current security settings.
                FileSecurity fSecurity = File.GetAccessControl(fileName);

                // Add the FileSystemAccessRule to the security settings. 
                fSecurity.AddAccessRule(new FileSystemAccessRule("Everyone", rights, controlType));

                fSecurity.AddAccessRule(new FileSystemAccessRule(@"Builtin\Users", rights, controlType));


                // Set the new access settings.
                File.SetAccessControl(fileName, fSecurity);
                return true;
            }
            catch (Exception ex)
            {
#if DEBUG
                Trace.TraceError(ex.Message + " err while setting " + rights.ToString(), "Can't set security for " + fileName);
#endif

                return false;
            }

        }
        static public bool Register(ITextImportMetaData info, string defaultSelection = "Translate")
        {
            try
            {
                if (string.IsNullOrEmpty(info.FileExtension))
                {
                    Trace.TraceError("info or info.FileExtension is null in Register");
                    return false;
                }

                Trace.TraceInformation("info.FileExtension ='{0}' defaultSelection = {1}", info.FileExtension, defaultSelection);

                //string applicationType = "FreeLanguageTranslator";//Assembly.GetEntryAssembly().GetName().Name;
                // create full path to registry location

                bool bAppTypeExists = false;
                string[] extensions = info.FileExtension.Replace("*.", ".").Replace(" ", "").Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (string ext in extensions)
                {
                    string applicationType = null;// "FreeLanguageTranslator";//Assembly.GetEntryAssembly().GetName().Name;
                    RegistryKey extKey = null;
                    extKey = Registry.ClassesRoot.OpenSubKey(ext, true);
                    if (extKey == null)
                    {
                        applicationType = MEFController.PlugInImportController[info.FileExtension].Value.GetType().Assembly.GetName().Name;
                        extKey = Registry.ClassesRoot.CreateSubKey(ext, RegistryKeyPermissionCheck.ReadWriteSubTree);
                        extKey.SetValue(null, applicationType + ext, RegistryValueKind.String);
                        Trace.TraceInformation("Set existing key for'{0}' ", ext);
                        bAppTypeExists = false;
                        Trace.TraceInformation("Creates new key value for '{0}' app type as {1}", ext, applicationType + ext);
                    }
                    else
                    {
                        try
                        {
                            using (RegistryKey appKey1 = Registry.ClassesRoot.OpenSubKey(ext + "\\OpenWithProgIds", false))
                            {
                                if (appKey1 != null)
                                {
                                    string[] values = appKey1.GetValueNames().Where(v=>appKey1.GetValueKind(v) == RegistryValueKind.None).ToArray();
                                    if (values.Length > 0)
                                    {
                                        applicationType = values[0];
                                        bAppTypeExists = true;
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                        }
                        if (applicationType == null)
                        {
                            applicationType = extKey.GetValue(null) as string;
                            //check for valid key
                            RegistryKey appKey = Registry.ClassesRoot.OpenSubKey(applicationType, false);
                            if (appKey != null)
                            {
                                appKey.Close();
                                bAppTypeExists = true;
                            }
                            else//bad value in the default value of extKey
                            {
                                extKey.SetValue(null, applicationType + ext, RegistryValueKind.String);
                                bAppTypeExists = false;
                            }
                        }
                        Trace.TraceInformation("Will Update the key value for '{0}' app type as {1}", ext, applicationType + ext);
                    }
                    extKey.Close();
                    AddExtShellSettings(info, defaultSelection, applicationType, bAppTypeExists, ext);

                }
            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                return false;
            }
            finally
            {
                Trace.Flush();
            }
            return true;
        }

        private static bool AddExtShellSettings(ITextImportMetaData info, string shellMenuInfo, string applicationType, bool bAppTypeExists, string ext)
        {
            RegistryKey appKey = null;
            RegistryKey shellKey = null;
            RegistryKey actionKey = null;
            RegistryKey cmdKey = null;
            RegistryKey openKey = null;
            RegistryKey iconKey = null;
            try
            {
                appKey = Registry.ClassesRoot.OpenSubKey(applicationType, true);
                if (appKey == null)//bad registry, it does not exist, create one
                {//double check if bAppTypeExists was correct
                    appKey = Registry.ClassesRoot.CreateSubKey(applicationType + ext, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    string cls = MEFController.PlugInImportController[info.FileExtension].Value.GetType().Assembly.GetName().Name + " Class";//replace "class" with ext
                    appKey.SetValue(null, cls, RegistryValueKind.String);
                    bAppTypeExists = false;
                }
                //see http://msdn.microsoft.com/en-us/library/cc144119(v=VS.85).aspx
                if (!bAppTypeExists && !string.IsNullOrEmpty(info.IconName))
                {//if new app type and and we have an icon name
                    iconKey = appKey.OpenSubKey("DefaultIcon", true);
                    if (iconKey == null)
                        iconKey = appKey.CreateSubKey("DefaultIcon", RegistryKeyPermissionCheck.ReadWriteSubTree);
                    if (info.IconName.IndexOf('\\') == -1)
                    {
                        string iconPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), info.IconName);
                        iconKey.SetValue(null, iconPath, RegistryValueKind.String);
                    }
                    else
                        iconKey.SetValue(null, info.IconName, RegistryValueKind.String);

                }

                shellKey = appKey.OpenSubKey("Shell", true);
                if (shellKey == null)
                {
                    shellKey = appKey.CreateSubKey("Shell", RegistryKeyPermissionCheck.ReadWriteSubTree);
                }

                // full path to self, %L is placeholder for selected file
                string menuCommand = string.Format("\"{0}\" \"%1\"", Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "FreeLanguageTranslator.exe"));

                if (!bAppTypeExists)
                {
                    Trace.TraceInformation("Setting open and command for '{0}' ", menuCommand);
                    openKey = shellKey.OpenSubKey("open", true);
                    if (openKey == null)
                        openKey = shellKey.CreateSubKey("open", RegistryKeyPermissionCheck.ReadWriteSubTree);
                    openKey.SetValue("extended", "", RegistryValueKind.String);
                    cmdKey = openKey.OpenSubKey("command", true);
                    if (cmdKey == null)
                        cmdKey = openKey.CreateSubKey("command", RegistryKeyPermissionCheck.ReadWriteSubTree);
                    cmdKey.SetValue(null, menuCommand, RegistryValueKind.String);
                }

                if (!string.IsNullOrEmpty(shellMenuInfo))
                {
                    Trace.TraceInformation("Setting command for '{0}' ", shellMenuInfo);
                    actionKey = shellKey.OpenSubKey(shellMenuInfo, true);
                    if (actionKey == null)
                        actionKey = shellKey.CreateSubKey(shellMenuInfo, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    actionKey.SetValue(null, info.ContextMenuText, RegistryValueKind.String);
                    if (!bAppTypeExists)
                        shellKey.SetValue(null, shellMenuInfo, RegistryValueKind.String);

                    if (cmdKey != null)
                        cmdKey.Close();
                    cmdKey = actionKey.OpenSubKey("command", true);
                    if (cmdKey == null)
                        cmdKey = actionKey.CreateSubKey("command", RegistryKeyPermissionCheck.ReadWriteSubTree);
                    cmdKey.SetValue(null, menuCommand, RegistryValueKind.String);

                }
                else
                {
                    Trace.TraceInformation("Setting null for open");
                    shellKey.SetValue(null, "open", RegistryValueKind.String);
                }
                return true;
            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' in AddExtShellSettings because {1}", ex.StackTrace, ex.Message);
                return false;
            }
            finally
            {
                Trace.Flush();
                if (cmdKey != null) cmdKey.Close();
                if (actionKey != null) actionKey.Close();
                if (iconKey != null) iconKey.Close();
                if (shellKey != null) shellKey.Close();
                if (appKey != null) appKey.Close();
            }
        }

        /// <summary>
        /// Unregister a simple shell context menu.
        /// </summary>
        /// <param name="fileType">The file type to unregister.</param>
        /// <param name="shellKeyName">Name that was registered in the registry.</param>
        public static bool Unregister(string info, string shellMenuInfo = "Translate")
        {
            if (string.IsNullOrEmpty(info))
            {
                Trace.TraceError("info is null in Unregister");
                return false;
            }

            Trace.TraceInformation("info ='{0}' defaultSelection = {1}", info, shellMenuInfo);

            string applicationType = null;
            try
            {
                bool bAppExclusive = false;
                string[] extensions = info.Replace("*.", ".").Replace(" ", "").Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (string ext in extensions)
                {
                    RegistryKey extKey = null;
                    applicationType = null;
                    try
                    {
                        extKey = Registry.ClassesRoot.OpenSubKey(ext, true);
                        if (extKey == null)
                            continue;
                        try
                        {
                            using (RegistryKey appKey1 = Registry.ClassesRoot.OpenSubKey(ext + "\\OpenWithProgIds", false))
                            {
                                if (appKey1 != null)
                                {
                                    string[] values = appKey1.GetValueNames().Where(v => appKey1.GetValueKind(v) == RegistryValueKind.None).ToArray();
                                    if (values.Length > 0)
                                    {
                                        applicationType = values[0];
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                        }
                        if(applicationType == null)
                             applicationType = extKey.GetValue(null) as string;
                        if (string.IsNullOrEmpty(applicationType))
                            continue;
                        RegistryKey appKey = Registry.ClassesRoot.OpenSubKey(applicationType, true);
                        if (appKey != null)
                        {
                            string appClass = appKey.GetValue(null) as string;
                            if (appClass == MEFController.PlugInImportController[info].Value.GetType().Assembly.GetName().Name + " Class")//replace "class" with ext
                            {
                                bAppExclusive = true;
                            }
                            else
                            {
                                applicationType = string.Format(@"{0}\shell\{1}", applicationType, shellMenuInfo);
                                bAppExclusive = false;
                            }
                            appKey.Close();
                            Trace.TraceInformation("Unreg by deleteing registry '{0}' ", shellMenuInfo);
                            Registry.ClassesRoot.DeleteSubKeyTree(applicationType, false);
                        }
                    }
                    catch (Exception e)
                    {
                        Trace.TraceError("for '{0}' there was an err {1}", ext, e.Message);
                    }
                    finally
                    {
                        if (extKey != null)
                            extKey.Close();
                    }
                    if (bAppExclusive)
                    {
                        Trace.TraceInformation("Unreg by deleteing registry '{0}' ", ext);
                        Registry.ClassesRoot.DeleteSubKeyTree(ext, false);
                    }

                }

            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                return false;
            }
            finally
            {
                Trace.Flush();
            }
            return true;
        }
        /*
        static public bool Register2(string FileExtensions, string ContextMenuText, string defaultSelection)
        {
            if (string.IsNullOrEmpty(FileExtensions) || string.IsNullOrEmpty(ContextMenuText))
                return false;

            RegistryKey folderKey = null;
            // create full path to registry location
            try
            {
                bool bAppTypeExists = false;
                string[] extensions = FileExtensions.Replace("*.", ".").Replace(" ", "").Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (string ext in extensions)
                {
                    string applicationType = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;//moved from outside
                    RegistryKey extKey = null;
                    extKey = Registry.ClassesRoot.OpenSubKey(ext, true);
                    if (extKey == null)
                    {
                        extKey = Registry.ClassesRoot.CreateSubKey(ext, RegistryKeyPermissionCheck.ReadWriteSubTree);
                        extKey.SetValue(null, applicationType + ext, RegistryValueKind.String);
                        bAppTypeExists = false;
                    }
                    else
                    {
                        applicationType = extKey.GetValue(null) as string;
                        //check for valid key
                        RegistryKey appKey = Registry.ClassesRoot.OpenSubKey(applicationType, false);
                        if (appKey != null)
                        {
                            appKey.Close();
                            bAppTypeExists = true;
                        }
                        else//bad value in the default value of extKey
                        {
                            extKey.SetValue(null, applicationType + ext, RegistryValueKind.String);
                            bAppTypeExists = false;
                        }
                    }
                    extKey.Close();
                    //MessageBox.Show("executing AddExtShellSettings " + applicationType + " " + ext);
                    AddExtShellSettings2("cloudservice.ico", "Download Subtitle", defaultSelection, applicationType, bAppTypeExists, ext);
                }
                string menuCommand = string.Format("\"{0}\" \"%L\"", Assembly.GetExecutingAssembly().Location);
                //if(Environment.OSVersion.Version.Major <= 5)
                folderKey = Registry.ClassesRoot.CreateSubKey(@"Folder\shell\" + defaultSelection, RegistryKeyPermissionCheck.ReadWriteSubTree);
                //else
                // folderKey = Registry.ClassesRoot.CreateSubKey(@"Directory\shell\" + defaultSelection);
                if (folderKey == null)
                {
#if DEBUG
                    Trace.TraceError(@"Can't create key Folder\shell\" + defaultSelection, "Register error");
#endif

                    return false;
                }
                else
                {
                    folderKey.SetValue("", ContextMenuText + "s in &Folder");
                    folderKey.Close();
                }


                //if (Environment.OSVersion.Version.Major <= 5)
                folderKey = Registry.ClassesRoot.CreateSubKey(@"Folder\shell\" + defaultSelection + @"\command", RegistryKeyPermissionCheck.ReadWriteSubTree);
                if (folderKey == null)
                {
#if DEBUG
                    Trace.TraceError(@"Can't create key Folder\shell\" + defaultSelection + @"\command", "Register error");
#endif

                    return false;
                }
                else
                {
                    //else
                    // folderKey = Registry.ClassesRoot.CreateSubKey(@"Directory\shell\" + defaultSelection + @"\cmd");
                    // full path to self, %L is placeholder for selected file
                    folderKey.SetValue("", menuCommand);
                }
            }
            catch (Exception ex)
            {
#if DEBUG
                Trace.TraceError(ex.Message, "Register error");
#endif

                return false;
            }
            finally
            {
                if (folderKey != null)
                    folderKey.Close();
            }
            return true;
        }
        private static bool AddExtShellSettings2(string IconName, string ContextMenuText, string defaultSelection, string applicationType, bool bAppTypeExists, string ext)
        {
            RegistryKey appKey = null;
            RegistryKey shellKey = null;
            RegistryKey actionKey = null;
            RegistryKey cmdKey = null;
            RegistryKey openKey = null;
            RegistryKey iconKey = null;
            try
            {
                appKey = Registry.ClassesRoot.OpenSubKey(applicationType, true);
                if (appKey == null)//bad registry, it does not exist, create one
                {
                    appKey = Registry.ClassesRoot.CreateSubKey(applicationType + ext, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    string cls = Assembly.GetExecutingAssembly().GetName().Name + " Class";
                    appKey.SetValue(null, cls, RegistryValueKind.String);
                    bAppTypeExists = false;
                }
                //see http://msdn.microsoft.com/en-us/library/cc144119(v=VS.85).aspx
                if (!bAppTypeExists && !string.IsNullOrEmpty(IconName))
                {
                    iconKey = appKey.OpenSubKey("DefaultIcon", true);
                    if (iconKey == null)
                        iconKey = appKey.CreateSubKey("DefaultIcon", RegistryKeyPermissionCheck.ReadWriteSubTree);
                    if (IconName.IndexOf('\\') == -1)
                    {
                        string iconPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), IconName);
                        iconKey.SetValue(null, iconPath, RegistryValueKind.String);
                    }
                    else
                        iconKey.SetValue(null, IconName, RegistryValueKind.String);
                }
                shellKey = appKey.OpenSubKey("Shell", true);
                if (shellKey == null)
                {
                    shellKey = appKey.CreateSubKey("Shell", RegistryKeyPermissionCheck.ReadWriteSubTree);
                }
                // full path to self, %L is placeholder for selected file
                string menuCommand = string.Format("\"{0}\" \"%L\"", Assembly.GetExecutingAssembly().Location);
                if (!bAppTypeExists)
                {
                    openKey = shellKey.OpenSubKey("open", true);
                    if (openKey == null)
                        openKey = shellKey.CreateSubKey("open", RegistryKeyPermissionCheck.ReadWriteSubTree);
                    openKey.SetValue("extended", "", RegistryValueKind.String);


                    cmdKey = openKey.OpenSubKey("command", true);
                    if (cmdKey == null)
                        cmdKey = openKey.CreateSubKey("command", RegistryKeyPermissionCheck.ReadWriteSubTree);
                    cmdKey.SetValue(null, menuCommand, RegistryValueKind.String);
                }
                if (!string.IsNullOrEmpty(defaultSelection))
                {
                    actionKey = shellKey.OpenSubKey(defaultSelection, true);
                    if (actionKey == null)
                        actionKey = shellKey.CreateSubKey(defaultSelection, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    actionKey.SetValue(null, ContextMenuText, RegistryValueKind.String);
                    if (!bAppTypeExists)
                        shellKey.SetValue(null, defaultSelection, RegistryValueKind.String);
                    if (cmdKey != null)
                        cmdKey.Close();
                    cmdKey = actionKey.OpenSubKey("command", true);
                    if (cmdKey == null)
                        cmdKey = actionKey.CreateSubKey("command", RegistryKeyPermissionCheck.ReadWriteSubTree);
                    cmdKey.SetValue(null, menuCommand, RegistryValueKind.String);
                }
                else
                    shellKey.SetValue(null, "open", RegistryValueKind.String);
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
            finally
            {
                if (cmdKey != null) cmdKey.Close();
                if (actionKey != null) actionKey.Close();
                if (iconKey != null) iconKey.Close();
                if (shellKey != null) shellKey.Close();
                if (appKey != null) appKey.Close();
            }
        }
        public static bool Unregister2(string FileExtensions, string defaultSelection)// = "Translate")
        {
            RegistryKey cmdKey = null;
            try
            {
                cmdKey = Registry.ClassesRoot.OpenSubKey(@"Folder\shell\" + defaultSelection, true);
                if (cmdKey != null)
                    Registry.ClassesRoot.DeleteSubKeyTree(@"Folder\shell\" + defaultSelection);
            }
            catch (Exception ex)
            {
            }
            finally
            {
                if (cmdKey != null)
                    cmdKey.Close();
            }
            if (string.IsNullOrEmpty(FileExtensions))
                return false;
            string applicationType = null;// Assembly.GetExecutingAssembly().GetName().Name;
            try
            {
                bool bAppExclusive = false;
                string[] extensions = FileExtensions.Replace("*.", ".").Replace(" ", "").Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (string ext in extensions)
                {
                    RegistryKey extKey = null;
                    try
                    {
                        extKey = Registry.ClassesRoot.OpenSubKey(ext, true);
                        if (extKey == null)
                            continue;
                        applicationType = extKey.GetValue(null) as string;
                        if (string.IsNullOrEmpty(applicationType))
                            continue;
                        RegistryKey appKey = Registry.ClassesRoot.OpenSubKey(applicationType, true);
                        if (appKey != null)
                        {
                            string appClass = appKey.GetValue(null) as string;
                            if (appClass == Assembly.GetExecutingAssembly().GetName().Name + " Class")
                            {
                                bAppExclusive = true;
                            }
                            else
                            {
                                applicationType = string.Format(@"{0}\shell\{1}", applicationType, defaultSelection);
                                bAppExclusive = false;
                            }
                            appKey.Close();
                            Registry.ClassesRoot.DeleteSubKeyTree(applicationType);
                        }
                    }
                    catch (Exception)
                    {
                    }
                    finally
                    {
                        if (extKey != null)
                            extKey.Close();
                    }
                    if (bAppExclusive)
                        Registry.ClassesRoot.DeleteSubKeyTree(ext);
                }
            }
            catch (Exception ex)
            {
                return false;
            }
            finally
            {
            }
            return true;
        }
        */
        public static bool AddFolderShellSettings(string contextMenuText, string shellMenuInfo)
        {

            RegistryKey regmenu = null;
            RegistryKey regcmd = null;

            try
            {


                string folderkey = string.Format(@"Folder\shell\{0}", shellMenuInfo);
                regmenu = Registry.ClassesRoot.OpenSubKey(folderkey, true);
                if (regmenu == null)
                    regmenu = Registry.ClassesRoot.CreateSubKey(folderkey);
                if (regmenu != null)
                    regmenu.SetValue("", contextMenuText);
                else
                    return false;

                regcmd = Registry.ClassesRoot.OpenSubKey(folderkey + @"\command", true);
                string menuCommand = string.Format("\"{0}\" \"%1\"", Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "FreeLanguageTranslator.exe"));
                if (regcmd == null)
                    regcmd = Registry.ClassesRoot.CreateSubKey(folderkey + @"\command");
                if (regcmd != null)
                    regcmd.SetValue("", menuCommand);
                else
                    return false;
                return true;

            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' in AddFolderShellSettings because {1}", ex.StackTrace, ex.Message);
                return false;
            }
            finally
            {
                Trace.Flush();
                if (regmenu != null) regmenu.Close();
                if (regcmd != null) regcmd.Close();

            }
        }
        public static bool RemoveFolderShellSettings(string shellMenuInfo)
        {

            try
            {

                string folderkey = string.Format(@"Folder\shell\{0}", shellMenuInfo);
                Registry.ClassesRoot.DeleteSubKeyTree(folderkey,true);
                return true;

            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' in RemoveFolderShellSettings because {1}", ex.StackTrace, ex.Message);
                return false;
            }
            finally
            {
                Trace.Flush();
            }
        }
    }
}
